package redisLog

import (
	"encoding/json"
	"fmt"
	"gitee.com/lv_baobao/gcore/helper"
	"gitee.com/lv_baobao/gcore/logHelper"
	"io/ioutil"
	"math"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

type RedisLogType int

const (
	Add    RedisLogType = 0
	Del    RedisLogType = 1
	Incr   RedisLogType = 2
	Expire RedisLogType = 3
	LPush  RedisLogType = 4
	LSet   RedisLogType = 5
	LRem   RedisLogType = 6
)

type RedisLogContent struct {
	LogType           RedisLogType
	RedisKey          string
	Content           interface{}
	SecondsOfDuration float64
	Count             int64
}

type RedisLogHelper struct {
	FileLock        sync.Mutex
	FileNameTemp    string
	FileName        string
	FileIndex       int
	MaxFileSize     int64
	MaxWriteSeconds float64
}

var redisLogOnce sync.Once
var redisLogHelper *RedisLogHelper

func NewRedisLogHelper() *RedisLogHelper {
	redisLogOnce.Do(func() {
		redisLogHelper = &RedisLogHelper{}
		redisLogHelper.FileNameTemp = "dblog/redisLogs/log"
		redisLogHelper.FileName = "dblog/redisLogs/log_0"
		redisLogHelper.MaxFileSize = 1024 * 50
		redisLogHelper.MaxWriteSeconds = 4
		redisLogHelper.FileIndex = redisLogHelper.GetLatestFileIndex()
		os.MkdirAll("dblog/redisLogs", 0766)
		os.MkdirAll("dblog/redisSenderLogs", 0766)
		redisLogHelper.startMoveFile()
		helper.NewScheduleHelper().Start("send redis log task", time.Second*1, func() {
			NewRedisDbLogSender().StartSendFile()
		})
	})
	return redisLogHelper
}

func (d *RedisLogHelper) WriteLog(logContent RedisLogContent) error {
	bytes, err := json.Marshal(logContent)
	if err != nil {
		return err
	}
	d.FileLock.Lock()
	defer d.FileLock.Unlock()
	fl, err := os.OpenFile(d.FileName, os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer fl.Close()
	_, err = fl.Write(bytes)
	fl.WriteString("\n")
	d.CheckNewFile(fl)
	return nil
}

func (d *RedisLogHelper) startMoveFile() {
	helper.NewScheduleHelper().Start("迁移文件", time.Second*1, func() {
		path, err := d.GetOldFirstFilePath()
		if err != nil {
			return
		}
		if path == "" {
			return
		}
		fl, err := os.OpenFile(path, os.O_RDONLY, 0644)
		if err != nil {
			return
		}
		defer fl.Close()
		stat, err := fl.Stat()
		if err != nil {
			return
		}
		if time.Now().Sub(stat.ModTime()).Seconds() > 3 {
			fmt.Println("find redis file", path)
			index := strings.LastIndex(path, "/")
			fileName := path[index+1 : len(path)]
			fl.Close()
			err = os.Rename(path, fmt.Sprintf("dblog/redisSenderLogs/%d_%s", time.Now().Unix(), fileName))
			if err != nil {
				logHelper.Error(err)
			}
		}
	})
}

func (d *RedisLogHelper) GetOldFirstFilePath() (string, error) {
	files, err := ioutil.ReadDir("dblog/redisLogs")
	if err != nil {
		return "", err
	}
	if len(files) == 0 {
		return "", nil
	}

	var tempFile = files[0]
	for _, file := range files {
		if file.ModTime().Before(tempFile.ModTime()) {
			tempFile = file
		}
	}
	filePath := fmt.Sprintf("dblog/redisLogs/%s", tempFile.Name())
	return filePath, nil
}

func (d *RedisLogHelper) CheckNewFile(fl *os.File) bool {
	stat, _ := fl.Stat()
	size := stat.Size()
	seconds := time.Now().Sub(stat.ModTime()).Seconds()
	filePath := fl.Name()
	index := strings.LastIndex(filePath, "/")
	fileName := filePath[index+1 : len(filePath)]
	if size > d.MaxFileSize || seconds > d.MaxWriteSeconds {
		//d.FileLock.Lock()
		//defer d.FileLock.Unlock()
		d.FileIndex++
		if d.FileIndex > math.MaxUint16 {
			d.FileIndex = 0
		}
		fl.Close()
		err := os.Rename(filePath, fmt.Sprintf("dblog/redisSenderLogs/%d_%s", time.Now().Unix(), fileName))
		if err != nil {
			logHelper.Error(err)
		}
		d.FileName = fmt.Sprintf("%s_%d", d.FileNameTemp, d.FileIndex)
		return true
	}
	return false
}

func (d *RedisLogHelper) GetLatestFileIndex() int {
	files, err := ioutil.ReadDir("dblog/redisLogs")
	if err != nil {
		return 0
	}
	var maxIndex = 0
	for _, file := range files {
		index := strings.LastIndex(file.Name(), "_")
		if index > -1 {
			s := file.Name()[index+1 : len(file.Name())]
			fileIndex, _ := strconv.Atoi(s)
			if fileIndex > maxIndex {
				maxIndex = fileIndex
			}
		}
	}
	fmt.Println("maxFileIndex", maxIndex)
	return maxIndex
}
